package com.onurokan.someone4me.controller;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;

import javax.imageio.ImageIO;
import javax.validation.Valid;

import org.im4java.core.ConvertCmd;
import org.im4java.core.IM4JavaException;
import org.im4java.core.IMOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.encoding.Md5PasswordEncoder;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.onurokan.someone4me.photo.dao.PhotoDao;
import com.onurokan.someone4me.photo.domain.Photo;
import com.onurokan.someone4me.user.dao.UserDao;
import com.onurokan.someone4me.user.domain.User;
import com.onurokan.someone4me.validator.PhotoValidator;

@Controller
public class FileUploadController {

	private final String ID_ENCODER_KEYWORD = "id_encoder_keyword";
	private final String PROFILE_DIRECTORY_PATH = "workspace_spring/someone4me/profile_photos/";
	private final String IMAGE_DIR = "/profile_photos/";
	private final String IMAGE_FORMAT = "jpg";

	@Autowired
	private Md5PasswordEncoder passwordEncoder;

	@Autowired
	UserDao userDao;

	@Autowired
	PhotoDao photoDao;

	@RequestMapping(value = "/profilePhoto", method = RequestMethod.GET)
	public String profilePhoto(ModelMap modelMap) {
		UserDetails userDetails = (UserDetails) SecurityContextHolder
				.getContext().getAuthentication().getPrincipal();
		String username = userDetails.getUsername();
		User user = userDao.loadUserByUsername(username);
		fillProfilePhotoPage(modelMap, user);
		return "profile_photo";
	}

	// TODO(onur): check file type by header.
	@RequestMapping(value = "/uploadFile", method = RequestMethod.POST)
	public String uploadFile(@Valid Photo newPhoto, BindingResult result,
			ModelMap modelMap) {

		if (result.hasErrors()) {
			return "invalidlink";
		}
		UserDetails userDetails = (UserDetails) SecurityContextHolder
				.getContext().getAuthentication().getPrincipal();
		String username = userDetails.getUsername();
		User user = userDao.loadUserByUsername(username);
		String dirName = passwordEncoder.encodePassword(
				user.getId().toString(), ID_ENCODER_KEYWORD);
		File userDir = new File(PROFILE_DIRECTORY_PATH + dirName);
		if (!userDir.exists()) {
			boolean success = userDir.mkdir();
			if (!success) {
				return "invalidlink";
			}
		}
		Set<Photo> photos = user.getPhotos();
		int rank = photos.size();
		if (rank < 0 || rank >= Photo.MAX_NUM_PHOTO) {
			return "invalidlink";
		}
		newPhoto.setRank(rank);
		newPhoto.setUser(user);
		// Set Id.
		photoDao.addPhoto(newPhoto);

		String imagePath = PROFILE_DIRECTORY_PATH + dirName + "/"
				+ newPhoto.getId() + "." + IMAGE_FORMAT;
		String tumbnailPath = PROFILE_DIRECTORY_PATH + dirName + "/t_"
				+ newPhoto.getId() + "." + IMAGE_FORMAT;
		BufferedImage bufferedImage;
		try {
			bufferedImage = ImageIO.read(newPhoto.getFile().getInputStream());
			ConvertCmd cmd = new ConvertCmd();
			IMOperation op = new IMOperation();
			op.addImage();
			op.resize(Photo.IMAGE_HEIGHT, Photo.IMAGE_WIDTH);
			op.addImage();
			cmd.run(op, bufferedImage, imagePath);
			op = new IMOperation();
			op.addImage();
			op.crop((int) newPhoto.getWidth(), (int) newPhoto.getHeight(),
					(int) newPhoto.getX(), (int) newPhoto.getY());
			op.addImage();
			cmd.run(op, imagePath, tumbnailPath);
		} catch (IOException e1) {
			photoDao.remove(newPhoto);
			e1.printStackTrace();
			return "invalidlink";
		} catch (InterruptedException e) {
			photoDao.remove(newPhoto);
			e.printStackTrace();
			return "invalidlink";
		} catch (IM4JavaException e) {
			photoDao.remove(newPhoto);
			e.printStackTrace();
			return "invalidlink";
		}

		photos.add(newPhoto);
		user.setPhotos(photos);
		userDao.saveUser(user);
		
		fillProfilePhotoPage(modelMap, user);
		return "profile_photo";
	}

	@RequestMapping(value = "/topFile", method = RequestMethod.POST)
	public String topFile(Photo topPhoto, BindingResult result,
			ModelMap modelMap) {
		if (result.hasErrors()) {
			return "invalidlink";
		}
		UserDetails userDetails = (UserDetails) SecurityContextHolder
				.getContext().getAuthentication().getPrincipal();
		String username = userDetails.getUsername();
		User user = userDao.loadUserByUsername(username);
		Set<Photo> photos = user.getPhotos();
		if (topPhoto.getRank() < photos.size() - 1) {
			for (Photo each : photos) {
				if (each.getRank() == topPhoto.getRank()) {
					each.setRank(photos.size() - 1);
				} else if (each.getRank() > topPhoto.getRank()) {
					each.setRank(each.getRank() - 1);
				}
			}
			user.setPhotos(photos);
			userDao.saveUser(user);
		}
		
		fillProfilePhotoPage(modelMap, user);
		return "profile_photo";
	}
	
	@RequestMapping(value = "/deleteFile", method = RequestMethod.POST)
	public String deleteFile(Photo deletePhoto, BindingResult result,
			ModelMap modelMap) {
		if (result.hasErrors()) {
			return "invalidlink";
		}
		UserDetails userDetails = (UserDetails) SecurityContextHolder
				.getContext().getAuthentication().getPrincipal();
		String username = userDetails.getUsername();
		User user = userDao.loadUserByUsername(username);
		String dirName = passwordEncoder.encodePassword(
				user.getId().toString(), ID_ENCODER_KEYWORD);
		Set<Photo> photos = user.getPhotos();
		for (Photo each : photos) {
			if (each.getRank() == deletePhoto.getRank()) {
				deletePhoto = each;
				break;
			} else if (each.getRank() > deletePhoto.getRank()) {
				each.setRank(each.getRank() - 1);
			}
		}
		photos.remove(deletePhoto);
		user.setPhotos(photos);
		String imagePath = PROFILE_DIRECTORY_PATH + dirName + "/"
				+ deletePhoto.getId() + "." + IMAGE_FORMAT;
		String tumbnailPath = PROFILE_DIRECTORY_PATH + dirName + "/t_"
				+ deletePhoto.getId() + "." + IMAGE_FORMAT;
		File deleteFile = new File(imagePath);
		deleteFile.delete();
		deleteFile = new File(tumbnailPath);
		deleteFile.delete();
		photoDao.remove(deletePhoto);
		userDao.saveUser(user);

		fillProfilePhotoPage(modelMap, user);
		return "profile_photo";
	}

	@InitBinder
	public void initBinder(WebDataBinder binder) {
		binder.setValidator(new PhotoValidator());
	}

	private void fillProfilePhotoPage(ModelMap modelMap, User user) {
		Set<Photo> photoSet = user.getPhotos();
		List<Photo> photos = new ArrayList<Photo>();
		photos.addAll(photoSet);
		Collections.sort(photos);
		String dirName = passwordEncoder.encodePassword(
				user.getId().toString(), ID_ENCODER_KEYWORD);
		List<String> photoPaths = new ArrayList<String>();
		List<String> captions = new ArrayList<String>();
		for (Photo photo : photos) {
			photoPaths.add(IMAGE_DIR + dirName + "/" + photo.getId() + "."
					+ IMAGE_FORMAT);
			captions.add(photo.getCaption());
		}
		modelMap.put("photos", photoPaths);
		modelMap.put("captions", captions);
		modelMap.put("newPhoto", new Photo());
		modelMap.put("topPhoto", new Photo());
		modelMap.put("deletePhoto", new Photo());
	}
}
